﻿using SocketWeaver.FixedMath;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace SocketWeaver.FPhysics2D
{
    [DefaultExecutionOrder(-10000)]
    public class FPhysics2DManager : MonoBehaviour
    {
        public bool autoInitialize = true;
        public bool autoUpdate = true;

        public FFloat fixedUpdateTime = FFloat.FromDivision(2, 100);
        public FVector2 gravity = new FVector2(FFloat.zero, -FFloat.FromDivision(98, 10));

        public int velocityIteration = 4;

        private void Awake()
        {
            if(autoInitialize)
            {
                Initialize();
            }
        }

        public void Initialize()
        {
            FPhysics2D.Initialize(gravity, RollbackAddRigidbody, RollbackRemoveRigidbody);

            FRigidbody2D[] fRigidbody2Ds = FindObjectsOfType<FRigidbody2D>().OrderBy(m => m.sceneIndex).ToArray();

            foreach (FRigidbody2D rigidbody2D in fRigidbody2Ds)
            {
                //Debug.Log("Init " + parallelRigidbody2D.name + " index=" + parallelRigidbody2D.sceneIndex);
                Debug.Log("Init " + rigidbody2D.name + " index=" + rigidbody2D.sceneIndex);

                rigidbody2D.AddPhysicsBody();
            }

            Time.fixedDeltaTime = (float)fixedUpdateTime;
        }

        private void OnDestroy()
        {
            FPhysics2D.CleanUp();
        }

        private void FixedUpdate()
        {
            if (autoUpdate)
            {
                OnUpdate(fixedUpdateTime);
            }
        }

        public void OnUpdate(FFloat deltaTime)
        {
            Step(fixedUpdateTime);
        }

        public void Step(FFloat deltaTime)
        {
            //using (new SProfiler($"==========ExcuteUserCallbacks========"))
            {
                FPhysics2D.ResetCollisionEvents();
                FPhysics2D.Step(deltaTime, velocityIteration);
                FPhysics2D.ExportFromEngine();
                FPhysics2D.InvokeCollisionEvents();
            }
        }

        //rollback
        void RollbackRemoveRigidbody(uint externalId, ushort bodyId)
        {
            
        }

        void RollbackAddRigidbody(uint externalId, ushort bodyId, IntPtr pBody2D)
        {

        }

        //
        public static GameObject Instantiate(GameObject prefab, FVector3 position, FQuaternion rotation)
        {
            GameObject go = Instantiate(prefab, (Vector3)position, (Quaternion)rotation);

            FTransform fTransform = go.GetComponent<FTransform>();

            fTransform.position = position;
            fTransform.rotation = rotation;

            FRigidbody2D fRigidbody2D = go.GetComponent<FRigidbody2D>();
            fRigidbody2D.AddPhysicsBody();

            return go;
        }

        public IEnumerable<FRigidbody2D> rigidbodies
        {
            get
            {
                var rbs = FPhysics2D._bodySortedList.Values;
                for(int i = 0; i < rbs.Count; i++)
                {
                    yield return rbs[i].rigidBody;
                }
            }
        }
    }
}
